/**
 *  Copyright 2013 University Pierre & Marie Curie - UMR CNRS 7606 (LIP6/MoVe)
 *  All rights reserved.   This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  Initial contributor:
 *    Lom M. Hillah - <lom-messan.hillah@lip6.fr>
 *
 *  Mailing list:
 *    lom-messan.hillah@lip6.fr
 */
package fr.lip6.msr4j.utils.db;

import org.neo4j.cypher.ExecutionEngine;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.graphdb.index.AutoIndexer;
import org.neo4j.kernel.logging.BufferingLogger;

import fr.lip6.msr4j.asf.datamodel.ASFProjectsCommitters;
import fr.lip6.msr4j.asf.utils.config.ASFProperties;

/**
 * NeO4J DB Handler for initial import. Not batch insertion-oriented.
 * 
 * @author lom
 * @since 0.0.1
 */
public abstract class Neo4JDBHandler extends DBHandler {
	protected static final double NANO = 1.0e9;
	protected static final double HALF = 0.5;
	protected static final int ALLOWED_EXEC_TIME1000 = 1000;
	
	private GraphDatabaseService graphDb;
	private ExecutionEngine engine;

	private AutoIndexer<Node> nodeAutoIndexer;
	private AutoIndexer<Relationship> relAutoIndexer;

	public Neo4JDBHandler(String dbPath) {
		super(dbPath);
	}

	@Override
	public void cleanDb() {
		if (graphDb != null) {
			engine.execute("START n = node(*), ref = node(0)  WHERE n<>ref DELETE n");
		}
	}

	@Override
	public void createDb() {
		logger.info("Creating Graph DB...");
		graphDb = new GraphDatabaseFactory().newEmbeddedDatabase(this.dbPath);
		registerShutdownHook(graphDb);
		engine = new ExecutionEngine(graphDb, new BufferingLogger());
		logger.info("Graph DB created.");
	}

	@Override
	public void shutDownDb() {
		graphDb.shutdown();
		logger.info("Graph DB shut down.");
	}

	protected void registerShutdownHook(final GraphDatabaseService gdb) {
		Runtime.getRuntime().addShutdownHook(new Thread() {
			@Override
			public void run() {
				gdb.shutdown();
				logger.info("Registered shutdown hook.");
			}
		});
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public Transaction beginTransaction() {
		return this.graphDb.beginTx();
	}
	@SuppressWarnings("hiding")
	@Override
	public <Transaction> void closeSuccessfullTransaction(Transaction tx) {
		((org.neo4j.graphdb.Transaction) tx).success();
		((org.neo4j.graphdb.Transaction) tx).finish();
	}

	public Node createNode() {
		return this.graphDb.createNode();
	}

	public Node createNode(Label... arg) {
		return this.graphDb.createNode(arg);
	}

	public void startAutoIndexingNodeProperty(String property) {
		nodeAutoIndexer = graphDb.index().getNodeAutoIndexer();
		nodeAutoIndexer.startAutoIndexingProperty(property);
		if (!nodeAutoIndexer.isEnabled()) {
			nodeAutoIndexer.setEnabled(true);
		}
	}

	protected void startAutoIndexingNodeProperties(String... properties) {
		for (String s : properties) {
			startAutoIndexingNodeProperty(s);
		}
	}

	public void startAutoIndexingRelProperty(String property) {
		relAutoIndexer = graphDb.index().getRelationshipAutoIndexer();
		relAutoIndexer.startAutoIndexingProperty(property);
		if (!relAutoIndexer.isEnabled()) {
			relAutoIndexer.setEnabled(true);
		}
	}

	public void startAutoIndexingRelProperties(String... properties) {
		for (String s : properties) {
			startAutoIndexingRelProperty(s);
		}
	}
	
	public void setPropertiesToIndex(String[] propToIndex) {
		startAutoIndexingNodeProperties(propToIndex);
		startAutoIndexingRelProperty(ASFProperties.WEIGHT);
	}

	public abstract void populateDB(ASFProjectsCommitters projects);

}
